/*
 * $Id$
 */
package ru.usu.gv.utils.exports;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;

import javax.media.j3d.Transform3D;
import javax.vecmath.Point3d;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import ru.usu.gv.core.GraphCanvas;
import ru.usu.gv.graph.Graph;
import ru.usu.gv.graph.Vertex;
import ru.usu.gv.styles.StyleFacade;
import ru.usu.gv.uicomponents.UIEdge;
import ru.usu.gv.uicomponents.UIGraph;
import ru.usu.gv.uicomponents.UIVertex;
import ru.usu.gv.utils.imports.IGraphXmlConstants;
import ru.usu.gv.utils.imports.XmlParseException;

/**
 * @author spupyrev
 * 16.01.2007
 * 
 * TODO: implement properly dir vs undir
 */
public class GraphExporter implements IGraphXmlConstants
{
	public static void saveGraph(UIGraph graph, String filePath) throws XmlParseException
	{
		try
		{
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder xmlBuilder = dbf.newDocumentBuilder();

			Document doc = xmlBuilder.newDocument();
			Element root = doc.createElement("graphml");
			root.setAttribute("xmlns", "http://graphml.graphdrawing.org/xmlns");
			root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
			root.setAttribute("xsi:schemaLocation", "http://graphml.graphdrawing.org/xmlns\nhttp://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd");

			doc.appendChild(root);

			root.appendChild(createGraphNode(doc, graph));
			root.appendChild(createViewNode(doc, graph));

			saveDocument(filePath, doc);
		}
		catch (XmlParseException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new XmlParseException(e);
		}
	}

	private static void saveDocument(String filePath, Document doc) throws Exception
	{
		File file = new File(filePath);

		FileWriter out = new FileWriter(file);
		// Use a Transformer for output
		TransformerFactory tFactory = TransformerFactory.newInstance();
		Transformer transformer = tFactory.newTransformer();
		transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		DOMSource source = new DOMSource(doc);
		StreamResult result = new StreamResult(out);
		transformer.transform(source, result);
		out.close();
	}

	private static Node createViewNode(Document doc, UIGraph graph)
	{
		Element viewNode = doc.createElement(VIEW_NODE);
		Transform3D transform = new Transform3D();
		GraphCanvas.getInstance().getTransformGroup().getTransform(transform);
		double[] matrix = new double[16];
		transform.get(matrix);

		for (int i = 0; i < matrix.length; i++)
		{
			Element subNode = doc.createElement("transform" + String.valueOf(i));
			subNode.setAttribute("value", String.valueOf(matrix[i]));

			viewNode.appendChild(subNode);
		}

		return viewNode;
	}

	private static Node createGraphNode(Document doc, UIGraph graph)
	{
		Element graphNode = doc.createElement(GRAPH_NODE);
		graphNode.setAttribute("id", graph.getId());
		graphNode.setAttribute(GRAPH_DIRECTED_ATTRIBUTE, String.valueOf(StyleFacade.isDirectedGraph()));

		int counter = 0;
		Map<Vertex, String> vertexToId = new HashMap();

		for (UIVertex vertex : graph.getVertexes())
		{
			Element vNode = doc.createElement(VERTEX_NODE);
			vNode.setAttribute(VERTEX_ID, String.valueOf(++counter));

			vertexToId.put(vertex.getVertex(), vNode.getAttribute(VERTEX_ID));

			if (vertex.getLabel() != null)
				vNode.setAttribute(VERTEX_LABEL, vertex.getLabel());
			vNode.setAttribute(VERTEX_COLOR, String.valueOf(vertex.getViewItem().getColor().getRGB()));
			vNode.setAttribute(VERTEX_TYPE, vertex.getVertexType().getId());
			vNode.setAttribute(VERTEX_X, String.valueOf(vertex.getPosition().getX()));
			vNode.setAttribute(VERTEX_Y, String.valueOf(vertex.getPosition().getY()));
			vNode.setAttribute(VERTEX_Z, String.valueOf(vertex.getPosition().getZ()));
			graphNode.appendChild(vNode);
		}

		for (UIEdge edge : graph.getEdges())
		{
			Element eNode = doc.createElement(EDGE_NODE);
			Vertex v1 = edge.getV1().getVertex();
			Vertex v2 = edge.getV2().getVertex();
			if (vertexToId.get(v1).compareTo(vertexToId.get(v2)) > 0)
			{
				v1 = edge.getV2().getVertex();
				v2 = edge.getV1().getVertex();
			}

			eNode.setAttribute(EDGE_BEGIN, vertexToId.get(v1));
			eNode.setAttribute(EDGE_END, vertexToId.get(v2));
			eNode.setAttribute(EDGE_COLOR, String.valueOf(edge.getViewItem().getColor().getRGB()));
			eNode.setAttribute(EDGE_TYPE, edge.getEdgeType().getId());

			for (Point3d point : edge.getViewItem().getControlPoints())
			{
				Element element = doc.createElement(EDGE_CONTROL_POINT);
				element.setAttribute(VERTEX_X, String.valueOf(point.x));
				element.setAttribute(VERTEX_Y, String.valueOf(point.y));
				element.setAttribute(VERTEX_Z, String.valueOf(point.z));
				eNode.appendChild(element);
			}

			graphNode.appendChild(eNode);
		}
		return graphNode;
	}

	public static void exportAsLJGraph(UIGraph graph, String filePath)
	{
		try
		{
			FileOutputStream stream = new FileOutputStream(new File(filePath));
			exportAsLJGraph(graph.getGraph(), new PrintStream(stream));
			stream.close();
		}
		catch (IOException e)
		{
			System.err.println("Error occured during exporting: " + e.getMessage());
		}
	}

	public static void exportToDot(UIGraph graph, String filePath)
	{
		try
		{
			FileOutputStream stream = new FileOutputStream(new File(filePath));
			exportToDot(graph.getGraph(), new PrintStream(stream));
			stream.close();
		}
		catch (IOException e)
		{
			System.err.println("Error occured during exporting: " + e.getMessage());
		}
	}

	public static void exportToSvg(UIGraph graph, String filePath)
	{
		try
		{
			FileOutputStream stream = new FileOutputStream(new File(filePath));
			new SVGExporter().export(graph, new PrintStream(stream));
			stream.close();
			
			System.out.println("svg file created");
		}
		catch (IOException e)
		{
			System.err.println("Error occured during exporting: " + e.getMessage());
		}
	}

	public static void exportAsLJGraph(Graph graph, PrintStream stream)
	{
		for (int i = 0; i < graph.getSize(); i++)
		{
			Vertex v = graph.getVertex(i);
			stream.println(constructLabel(v) + " " + v.getOutEdges().size() + " " + v.getInEdges().size());
			assert (v.getOutEdges().size() == v.getInEdges().size());
			for (int j = 0; j < v.getOutEdges().size(); j++)
			{
				String label = constructLabel(v.getOutEdges().get(j));
				stream.println("    > " + label);
			}

			for (int j = 0; j < v.getInEdges().size(); j++)
			{
				String label = constructLabel(v.getInEdges().get(j));
				stream.println("    < " + label);
			}
		}
	}

	public static void exportToDot(Graph graph, PrintStream stream)
	{
		stream.println("digraph G {");
		for (int i = 0; i < graph.getSize(); i++)
		{
			Vertex v = graph.getVertex(i);
			String iLabel = constructLabel(v);
			for (int j = 0; j < v.getOutEdges().size(); j++)
			{
				String jLabel = constructLabel(v.getOutEdges().get(j));
				stream.println("\"" + iLabel + "\" -> \"" + jLabel + "\"");
			}
		}
		stream.println("}");
	}

	private static String constructLabel(Vertex u)
	{
		UIVertex uiVertex = u.findUIVertex();
		String label = (uiVertex != null && uiVertex.getLabel() != null ? uiVertex.getLabel() : "" + u.getIndex());
		return label;
	}

}
